在 Day08 內容裡,我們已經理解了 Properties 如同應用程式的「遙控器」,也認識了 Profiles 這個強大的多環境管理機制。現在,是時候學習如何將這些設定真正地「連接」到我們的 Java 程式碼中,讓應用程式根據我們的配置來運作。
今天將深入程式碼,掌握將配置注入到應用程式中的各種方法,並學習處理更複雜的配置場景與最佳實踐。
Spring Boot 提供了多種靈活的方式來讀取設定檔中的屬性。我們將介紹三種最主要的方法,從最簡單到功能最強大的逐一解析。
假設我們有以下的 application.properties 設定:
app.name=My Awesome App
app.version=1.0.0
app.description=這是一個很棒的應用程式!
@Value — 注入單一值@Value 註解 (Annotation) 是最直接、最簡單的方式,適合用來讀取單一的屬性值。
使用方式:
您只需要在類別的屬性 (Field) 上方加上 @Value("${屬性名稱}") 即可。
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component // 標記為 Spring 的組件,讓 Spring 容器管理它
public class AppInfo {
    @Value("${app.name}")
    private String appName;
    @Value("${app.version}")
    private String appVersion;
    // 您也可以在這裡使用預設值
    @Value("${app.author:Default Author}")
    private String appAuthor;
    public void printAppInfo() {
        System.out.println("應用程式名稱: " + appName);
        System.out.println("應用程式版本: " + appVersion);
        System.out.println("應用程式作者: " + appAuthor); // 如果設定檔沒有 app.author,會顯示 "Default Author"
    }
}
@Value 註解 (Annotation),顯得雜亂且不易管理。Environment 抽象 — 動態存取Spring 的 Environment 是一個介面 (Interface),它代表了應用程式執行時的環境。您可以透過它以程式化的方式,動態地取得任何屬性值。
使用方式:
透過依賴注入 (Dependency Injection) 取得 Environment 物件,然後呼叫 getProperty() 方法。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
@Service
public class MyDynamicService {
    @Autowired
    private Environment env;
    public String getAppName() {
        // 直接透過 key 來取得屬性值
        return env.getProperty("app.name");
    }
    public String getAppDescription() {
        // 也可以提供一個預設值
        return env.getProperty("app.description", "這是一個預設的描述。");
    }
}
getProperty() 回傳的都是字串 (String),您需要手動轉型,且如果屬性名稱拼錯,編譯器不會報錯,只會在執行時回傳 null。@ConfigurationProperties — 類型安全的結構化綁定 (👍 強烈推薦)這是 Spring Boot 最推薦的方式。@ConfigurationProperties 可以將一組相關的屬性,直接「綁定」到一個 Java 物件 (POJO) 上,實現類型安全且結構化的配置管理。
使用步驟:
步驟 1:建立一個設定類別 (Configuration Class)
這個類別的屬性 (Field) 名稱必須和設定檔中的 key 對應。
import org.springframework.boot.context.properties.ConfigurationProperties;
// 將所有以 "app" 為前綴的屬性綁定到這個類別上
@ConfigurationProperties(prefix = "app")
public class AppConfig {
    private String name;
    private String version;
    private String description;
    // 必須提供對應的 Getter 和 Setter 方法!
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getVersion() { return version; }
    public void setVersion(String version) { this.version = version; }
    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }
}
注意:Spring Boot 會自動進行駝峰式 (camelCase) 和 Kebab-case 的轉換。例如,設定檔中的 app.welcome-message 會自動對應到 Java 類別中的 welcomeMessage 屬性 (Field)。
步驟 2:啟用這個設定類別
要讓 Spring Boot 知道這個類別的存在,您有兩種方式:
@Component 註解 (Annotation),讓它成為一個 Spring Bean。import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component // 讓 Spring 掃描並註冊這個 Bean
@ConfigurationProperties(prefix = "app")
public class AppConfig { /* ... 省略 ... */ }
@EnableConfigurationProperties 來明確指定要啟用的設定類別。import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(AppConfig.class) // 明確啟用 AppConfig
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
步驟 3:在其他地方注入並使用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AppService {
    private final AppConfig appConfig;
    // 透過建構子注入,這是 Spring 推薦的最佳實踐
    @Autowired
    public AppService(AppConfig appConfig) {
        this.appConfig = appConfig;
    }
    public void displayAppConfig() {
        System.out.println("從 AppConfig 讀取到的名稱: " + appConfig.getName());
        System.out.println("從 AppConfig 讀取到的版本: " + appConfig.getVersion());
    }
}
abc 設給一個 Integer 屬性),應用程式啟動時會直接報錯。@ConfigurationProperties 有很好的支援,可以提供自動完成和語法檢查。@ConfigurationProperties 的強大之處在於它能輕鬆處理複雜的資料結構。
處理列表 (List)
假設您想設定一個支援的伺服器列表。
application.yml (推薦的格式)
server-list:
  hosts:
    - "server1.example.com"
    - "server2.example.com"
    - "server3.example.com"
application.properties (語法較繁瑣)
server-list.hosts[0]=server1.example.com
server-list.hosts[1]=server2.example.com
server-list.hosts[2]=server3.example.com
對應的 Java 設定類別
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@ConfigurationProperties(prefix = "server-list")
public class ServerListConfig {
    private List<String> hosts;
    public List<String> getHosts() { return hosts; }
    public void setHosts(List<String> hosts) { this.hosts = hosts; }
}
Spring Boot 會自動將設定檔中的多個值,綁定到 List<String> 結構中。
現在,讓我們將 Profiles 的概念與實際程式碼結合起來。
場景:我們希望在開發環境 (dev) 使用內嵌的 H2 資料庫方便快速啟動,而在正式環境 (prod) 則連接到外部的 MySQL 資料庫。
步驟 1:建立多個設定檔
application.properties (基礎設定)
# 預設啟用 dev 環境
spring.profiles.active=dev
app.name=My App
application-dev.properties (開發環境專用)
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
application-prod.properties (正式環境專用)
spring.datasource.url=jdbc:mysql://prod-db.example.com:3306/maindb
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.username=prod_user
spring.datasource.password=ToughPassword!
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
步驟 2:在程式碼中根據 Profile 載入不同的 Bean
我們可以使用 @Profile 註解 (Annotation) 來告訴 Spring,某個 Bean 只在特定的 Profile 被啟用時才需要被建立。
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
// 一個設定類別的容器
@Configuration
public class DataSourceConfig {
    @Profile("dev") // 只有在 dev profile 啟用時,這個 Bean 才被建立
    public void devDatabaseBean() {
        System.out.println("======================================");
        System.out.println("      DEV 環境已啟用,使用 H2 資料庫");
        System.out.println("======================================");
    }
    @Profile("prod") // 只有在 prod profile 啟用時,這個 Bean 才被建立
    public void prodDatabaseBean() {
        System.out.println("**************************************");
        System.out.println("      PROD 環境已啟用,連接 MySQL 資料庫");
        System.out.println("**************************************");
    }
}
步驟 3:啟用不同的 Profile
application.properties 中設定了 spring.profiles.active=dev,您會在控制台看到 "DEV 環境已啟用" 的訊息。prod 環境:java -jar myapp.jar --spring.profiles.active=prod
執行後,您會在控制台看到 "PROD 環境已啟用" 的訊息。命令列參數的優先級高於設定檔中的設定。
屬性覆寫的完整優先順序:請記住這個順序,當您發現設定不如預期時,這會是除錯的關鍵。
application-prod.properties)application.properties)大小寫敏感性:設定檔中的 key (如 app.name) 是大小寫不敏感的 (但建議統一小寫),而 @Value 中的佔位符 (Placeholders) ${app.name} 則是大小寫敏感的。
中文亂碼問題:請確保您所有的 .properties 或 .yml 檔案都使用 UTF-8 編碼儲存,以避免中文顯示為亂碼。
敏感資訊管理:絕對不要將資料庫密碼、API 金鑰等敏感資訊直接寫在設定檔並提交到版本控制系統 (如 Git)。最佳實踐是透過環境變數或專門的密鑰管理服務 (如 HashiCorp Vault, AWS Secrets Manager) 在部署時注入。